//
//  LeancloudUtils.swift
//  imprint
//
//  Created by 小柠檬🍋 on 2025/1/8.
//

/**
 *   leancloud
 */
import Foundation
import LeanCloud

class LeancloudManager {
    static let instance = LeancloudManager()
    private let tag = "Leancloud"

    func initLeanCloud() {
        do {
            try LCApplication.default.set(
                id: "8DuRL47t3xm6MqMVEUKRiwu5-gzGzoHsz",
                key: "TV4CcOQn9bq2waCatomj78Pb",
                serverURL: "https://8durl47t.lc-cn-n1-shared.com")
            showLog("initLeanCloud初始化完成")
        } catch {
            showLog("initLeanCloud初始化失败：" + (error as NSError).localizedDescription)
        }
    }
    
    //查询单条数据
    func getFirst<M: Codable>(query: LCQuery, modeType: M.Type) async throws-> M? {
        try await withCheckedThrowingContinuation({ continuation in
            query.getFirst{ result in
                switch result {
                case .success(object: let bean):
                    guard let dict = bean.jsonValue as? [String: Any] else {
                        continuation.resume(throwing: MyError.getJsonFaild)
                        return
                    }
                    guard let model = try? fromJson(dict, M.self) else {
                        //类型转换失败
                        continuation.resume(throwing: MyError.getJsonFaild)
                        return
                    }
                    //最终返回模型数据
                    continuation.resume(returning: model)
                    break
                case .failure(error: let error):
                    if error.code == 101 {
                        //没有数据
                        continuation.resume(returning: nil)
                        return
                    }
                    continuation.resume(throwing: error)
                    break
                }
            }
        })
    }
    
    //查询数据
    func getList<M: Codable>(query: LCQuery, modeType: M.Type) async throws-> [M] {
        try await withCheckedThrowingContinuation({ continuation in
            query.find{ result in
                switch result {
                case .success(objects: let list):
                    do {
                        let models = try list.compactMap { bean -> M? in
                            guard let dict = bean.jsonValue as? [String: Any] else {
                                throw MyError.getJsonFaild
                            }
                            guard let model = try? fromJson(dict, M.self) else {
                                //类型转换失败
                                throw MyError.getJsonFaild
                            }
                            return model
                        }
                        
                        // 如果到这里没有抛出错误，则成功解码所有模型
                        continuation.resume(returning: models)
                    } catch {
                        // 如果有任何错误，包括解码错误或类型转换错误，都在这里处理
                        continuation.resume(throwing: error)
                    }
                    break
                case .failure(error: let error):
                    continuation.resume(throwing: error)
                    break
                }
            }
        })
    }
    
    /**
     *  保存数据
     */
    func save(todo: ICloudTable) async throws -> Void {
        try await withCheckedThrowingContinuation { continuation in
            do {
                let todoObject: LCObject
                if let objectId = todo.objectId, !objectId.isEmpty {
                    todoObject = LCObject(className: todo.getCloudTable(), objectId: objectId)
                } else {
                    todoObject = LCObject(className: todo.getCloudTable())
                }
                for (label, value) in todo.doCustomExcluded() {
                    if let label = label, let lcValue = convertToLCValue(value) {
                        try todoObject.set(label, value: lcValue)
                        
                    }
                }
                todoObject.save { result in
                    switch result {
                    case .success:
                        continuation.resume(returning: ())
                    case .failure(let error):
                        continuation.resume(throwing: error)
                    }
                }
            } catch let error {
                continuation.resume(throwing: error)
            }
        }
    }
    
    /**
     *  保存数据并且返回id
     */
    func saveToId(todo: ICloudTable) async throws -> String? {
        try await withCheckedThrowingContinuation { continuation in
            do {
                let todoObject: LCObject
                if let objectId = todo.objectId, !objectId.isEmpty {
                    todoObject = LCObject(className: todo.getCloudTable(), objectId: objectId)
                } else {
                    todoObject = LCObject(className: todo.getCloudTable())
                }
                for (label, value) in todo.doCustomExcluded() {
                    if let label = label, let lcValue = convertToLCValue(value) {
                        try todoObject.set(label, value: lcValue)
                        
                    }
                }
                todoObject.save { result in
                    switch result {
                    case .success:
                        continuation.resume(returning: (todoObject.objectId?.value))
                    case .failure(let error):
                        continuation.resume(throwing: error)
                    }
                }
            } catch let error {
                continuation.resume(throwing: error)
            }
        }
    }
    
    
    /**
     *  删除数据
     */
    func del(todo: ICloudTable) async throws -> Void {
        try await withCheckedThrowingContinuation { continuation in
            do {
                
                let todoObject: LCObject
                if let objectId = todo.objectId, !objectId.isEmpty {
                    todoObject = LCObject(className: todo.getCloudTable(), objectId: objectId)
                } else {
                    throw MyError.checkParameter(tag, "objectId 为空")
                }
                todoObject.delete { result in
                    switch result {
                    case .success:
                        continuation.resume(returning: ())
                    case .failure(let error):
                        continuation.resume(throwing: error)
                    }
                }
            } catch let error {
                continuation.resume(throwing: error)
            }
        }
    }
    
    /**
     *  增量
     */
    func incrementKey(className: String, objectId: String, key: String, value: LCNumberConvertible) async throws -> Void {
        try await withCheckedThrowingContinuation { continuation in
            do {
                let todoObject = LCObject(className: className, objectId: objectId)
                try todoObject.increase(key, by: value)
                todoObject.save() { result in
                    switch result {
                    case .success:
                        continuation.resume(returning: ())
                    case .failure(let error):
                        continuation.resume(throwing: error)
                    }
                }
            } catch let error {
                continuation.resume(throwing: error)
            }
        }
    }
    
    func convertToLCValue(_ value: Any?) -> LCValueConvertible? {
        if value == nil {
            return nil
        }
        switch value {
        case let mValue as String:
            return mValue
        case let mValue as Int:
            return mValue.lcValue
        case let mValue as UInt:
            return mValue
        case let mValue as Int8:
            return mValue
        case let mValue as UInt8:
            return mValue
        case let mValue as Int16:
            return mValue
        case let mValue as Int16:
            return mValue
        case let mValue as UInt16:
            return mValue
        case let mValue as Int32:
            return mValue
        case let mValue as UInt32:
            return mValue
        case let mValue as Int64:
            return mValue
        case let mValue as UInt64:
            return mValue.lcValue
        case let mValue as Float:
            return mValue
        case let mValue as Double:
            return mValue
        case let mValue as Data:
            return mValue
        case let mValue as Date:
            return mValue
        case let mValue as [LCValueConvertible]:
            return LCArray(mValue)
        case let mValue as [Codable]:
            return toArrayLC(mValue)
        case let mValue as Bool:
            return mValue
        case let mValue as LCValueConvertible:
            return mValue
        case let mValue as LCObject:
            return mValue
        case let mValue as ICloudTable:
            if let dict =  toDictLC(mValue) {
                return LCDictionary(dict)
            }
            return nil
        case let mValue as Codable:
            if let dict =  toDictLC(mValue) {
                return LCDictionary(dict)
            }
            return nil
        default:
            //转换为字典
            return nil
        }
    }
    
    //对象 转化为 字典, 会排除不必要的字段
    //反射遍历
    func toDictLC(_ object: ICloudTable) -> [String: LCValueConvertible]? {
        let dict = object.doCustomExcluded()
        var properties: [String: LCValueConvertible] = [:]
        for (label, value) in dict {
            if let label = label, let value = value, let lcValue = convertToLCValue(value) {
                properties[label] = lcValue
            }
        }
        if properties.isEmpty {
            return nil
        }
        return properties
    }
    
    //对象 转化为 字典
    //反射遍历
    func toDictLC(_ object: Codable) -> [String: LCValueConvertible]? {
        var properties: [String: LCValueConvertible] = [:]
        let mirror = Mirror(reflecting: object)
        for (label, value) in mirror.children {
            //注意这里的反射出来的结果永远不也不好nil,所以判断properties是否为空
            if let label = label, let lcValue = convertToLCValue(value) {
                properties[label] = lcValue
            }
        }
        if properties.isEmpty {
            return nil
        }
        return properties
    }
    
    // 数组转换 LCArray
    func toArrayLC(_ array: [Codable]) -> LCArray {
        var newArray : [LCValueConvertible] = []
        array.forEach { value in
            if let lcValue = convertToLCValue(value) {
                newArray.append(lcValue)
            }
        }
        return LCArray(newArray)
    }
}
